"""Core classes of LLVM.

The llvm.core module contains classes and constants required to build the
in-memory intermediate representation (IR) data structures."""


import llvm             # top-level, for common stuff
import _core            # C wrappers
from _util import *     # utility functions


#===----------------------------------------------------------------------===
# Enumerations
#===----------------------------------------------------------------------===


# type kinds
TYPE_VOID       = 0
TYPE_FLOAT      = 1
TYPE_DOUBLE     = 2
TYPE_X86_FP80   = 3
TYPE_FP128      = 4
TYPE_PPC_FP128  = 5
TYPE_LABEL      = 6
TYPE_INTEGER    = 7
TYPE_FUNCTION   = 8
TYPE_STRUCT     = 9
TYPE_ARRAY      = 10
TYPE_POINTER    = 11
TYPE_OPAQUE     = 12
TYPE_VECTOR     = 13

# calling conventions
CC_C            = 0
CC_FASTCALL     = 8
CC_COLDCALL     = 9
CC_X86_STDCALL  = 64
CC_X86_FASTCALL = 65

# int predicates
IPRED_EQ        = 32
IPRED_NE        = 33
IPRED_UGT       = 34
IPRED_UGE       = 35
IPRED_ULT       = 36
IPRED_ULE       = 37
IPRED_SGT       = 38
IPRED_SGE       = 39
IPRED_SLT       = 40
IPRED_SLE       = 41

# real predicates
RPRED_FALSE     = 0
RPRED_OEQ       = 1
RPRED_OGT       = 2
RPRED_OGE       = 3
RPRED_OLT       = 4
RPRED_OLE       = 5
RPRED_ONE       = 6
RPRED_ORD       = 7
RPRED_UNO       = 8
RPRED_UEQ       = 9
RPRED_UGT       = 10
RPRED_UGE       = 11
RPRED_ULT       = 12
RPRED_ULE       = 13
RPRED_UNE       = 14
RPRED_TRUE      = 15

# linkages
LINKAGE_EXTERNAL    = 0
LINKAGE_LINKONCE    = 1
LINKAGE_WEAK        = 2
LINKAGE_APPENDING   = 3
LINKAGE_INTERNAL    = 4
LINKAGE_DLLIMPORT   = 5
LINKAGE_DLLEXPORT   = 6
LINKAGE_EXTERNAL_WEAK = 7
LINKAGE_GHOST       = 8

# visibility
VISIBILITY_DEFAULT  = 0
VISIBILITY_HIDDEN   = 1
VISIBILITY_PROTECTED = 2

# parameter attributes
ATTR_ZEXT           = 1
ATTR_SEXT           = 2
ATTR_NO_RETURN      = 4
ATTR_IN_REG         = 8
ATTR_STRUCT_RET     = 16
ATTR_NO_UNWIND      = 32
ATTR_NO_ALIAS       = 64
ATTR_BY_VAL         = 128
ATTR_NEST           = 256
ATTR_READ_NONE      = 512
ATTR_READONLY       = 1024


#===----------------------------------------------------------------------===
# Module
#===----------------------------------------------------------------------===


class Module(llvm.Ownable):
    """A Module instance stores all the information related to an LLVM module. 
       
    Modules are the top level container of all other LLVM Intermediate
    Representation (IR) objects. Each module directly contains a list of
    globals variables, a list of functions, a list of libraries (or
    other modules) this module depends on, a symbol table, and various
    data about the target's characteristics.

    Construct a Module only using the static methods defined below, *NOT*
    using the constructor. A correct usage is:

    module_obj = Module.new('my_module')
    """

    @staticmethod
    def new(id):
        """Create a new Module instance.

        Creates an instance of Module, having the id `id'.
        """
        return Module(_core.LLVMModuleCreateWithName(id))

    def __init__(self, ptr):
        """DO NOT CALL DIRECTLY.

        Use the static method `Module.new' instead.
        """
        llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeModule)

    def __str__(self):
        """Text representation of a module.

        Returns the textual representation (`llvm assembly') of the
        module. Use it like this:

        ll = str(module_obj)
        print module_obj     # same as `print ll'
        """
        return _core.LLVMDumpModuleToString(self.ptr)

    def __eq__(self, rhs):
        if isinstance(rhs, Module):
            return str(self) == str(rhs)
        else:
            return False

    def _get_target(self):
        return _core.LLVMGetTarget(self.ptr)

    def _set_target(self, value):
        return _core.LLVMSetTarget(self.ptr, value)

    target = property(_get_target, _set_target,
        """The target triple string describing the target host."""
    )

    def _get_data_layout(self):
        return _core.LLVMGetDataLayout(self.ptr)

    def _set_data_layout(self, value):
        _core.LLVMSetDataLayout(self.ptr, value)

    data_layout = property(_get_data_layout, _set_data_layout,
        """The data layout string for the module's target platform.

        The data layout strings is an encoded representation of
        the type sizes and alignments expected by this module.
        """
    )

    def add_type_name(self, name, ty):
        """Map a string to a type.

        Similar to C's struct/typedef declarations. Returns True
        if entry already existed (in which case nothing is changed),
        False otherwise.
        """
        check_is_type(ty)
        return _core.LLVMAddTypeName(self.ptr, name, ty.ptr) != 0

    def delete_type_name(self, name):
        """Removes a named type.

        Undoes what add_type_name() does.
        """
        _core.LLVMDeleteTypeName(self.ptr, name)

    def add_global_variable(self, ty, name):
        """Add a global variable of given type with given name."""
        return GlobalVariable.new(self, ty, name)

    def get_global_variable_named(self, name):
        """Return a GlobalVariable object for the given name."""
        return GlobalVariable.get(self, name)

    @property
    def global_variables(self):
        """All global variables in this module.

        This property returns a generator that yields GlobalVariable
        objects. Use it like this:
          for gv in module_obj.global_variables:
            # gv is an instance of GlobalVariable
            # do stuff with gv
        """
        return wrapiter(_core.LLVMGetFirstGlobal, _core.LLVMGetNextGlobal,
            self.ptr, GlobalVariable, [self])

    def add_function(self, ty, name):
        """Add a function of given type with given name."""
        return Function.new(self, ty, name)

    def get_function_named(self, name):
        """Return a Function object representing function with given name."""
        return Function.get(self, name)

    @property
    def functions(self):
        """All functions in this module.

        This property returns a generator that yields Function objects.
        Use it like this:
          for f in module_obj.functions:
            # f is an instance of Function
            # do stuff with f
        """
        return wrapiter(_core.LLVMGetFirstFunction, 
            _core.LLVMGetNextFunction, self.ptr, Function, [self])

    def verify(self):
        """Verify module.

        Checks module for errors. Raises `llvm.LLVMException' on any
        error."""
        ret = _core.LLVMVerifyModule(self.ptr)
        if ret != "":
            raise llvm.LLVMException, ret


#===----------------------------------------------------------------------===
# Types
#===----------------------------------------------------------------------===


class Type(object):
    """Represents a type, like a 32-bit integer or an 80-bit x86 float.

    Use one of the static methods to create an instance. Example:
      ty = Type.double()
    """

    @staticmethod
    def int(bits=32):
        """Create an integer type having the given bit width."""
        if bits == 1:
            return _make_type(_core.LLVMInt1Type(), TYPE_INTEGER)
        elif bits == 8:
            return _make_type(_core.LLVMInt8Type(), TYPE_INTEGER)
        elif bits == 16:
            return _make_type(_core.LLVMInt16Type(), TYPE_INTEGER)
        elif bits == 32:
            return _make_type(_core.LLVMInt32Type(), TYPE_INTEGER)
        elif bits == 64:
            return _make_type(_core.LLVMInt64Type(), TYPE_INTEGER)
        else:
            bits = int(bits) # bits must be an int
            return _make_type(_core.LLVMIntType(bits), TYPE_INTEGER)
    
    @staticmethod
    def float():
        """Create a 32-bit floating point type."""
        return _make_type(_core.LLVMFloatType(), TYPE_FLOAT)

    @staticmethod
    def double():
        """Create a 64-bit floating point type."""
        return _make_type(_core.LLVMDoubleType(), TYPE_DOUBLE)

    @staticmethod
    def x86_fp80():
        """Create a 80-bit x86 floating point type."""
        return _make_type(_core.LLVMX86FP80Type(), TYPE_X86_FP80)

    @staticmethod
    def fp128():
        """Create a 128-bit floating point type (with 112-bit
        mantissa)."""
        return _make_type(_core.LLVMFP128Type(), TYPE_FP128)

    @staticmethod
    def ppc_fp128():
        """Create a 128-bit floating point type (two 64-bits)."""
        return _make_type(_core.LLVMPPCFP128Type(), TYPE_PPC_FP128)

    @staticmethod
    def function(return_ty, param_tys, var_arg=False):
        """Create a function type.

        Creates a function type that returns a value of type
        `return_ty', takes arguments of types as given in the iterable
        `param_tys'. Set `var_arg' to True (default is False) for a
        variadic function."""
        check_is_type(return_ty)
        var_arg = 1 if var_arg else 0 # convert to int
        params = unpack_types(param_tys)
        return _make_type(_core.LLVMFunctionType(return_ty.ptr, params, 
                    var_arg), TYPE_FUNCTION)

    @staticmethod
    def struct(element_tys): # not packed
        """Create a (unpacked) structure type.

        Creates a structure type with elements of types as given in the
        iterable `element_tys'. This method creates a unpacked
        structure. For a packed one, use the packed_struct() method."""
        elems = unpack_types(element_tys)
        return _make_type(_core.LLVMStructType(elems, 0), TYPE_STRUCT)

    @staticmethod
    def packed_struct(element_tys):
        """Create a (packed) structure type.

        Creates a structure type with elements of types as given in the
        iterable `element_tys'. This method creates a packed
        structure. For an unpacked one, use the struct() method."""
        elems = unpack_types(element_tys)
        return _make_type(_core.LLVMStructType(elems, 1), TYPE_STRUCT)

    @staticmethod
    def array(element_ty, count):
        """Create an array type.

        Creates a type for an array of elements of type `element_ty',
        having 'count' elements."""
        check_is_type(element_ty)
        count = int(count) # must be an int
        return _make_type(_core.LLVMArrayType(element_ty.ptr, count), 
                    TYPE_ARRAY)

    @staticmethod
    def pointer(pointee_ty, addr_space=0):
        """Create a pointer type.

        Creates a pointer type, which can point to values of type
        `pointee_ty', in the address space `addr_space'."""
        check_is_type(pointee_ty)
        addr_space = int(addr_space) # must be an int
        return _make_type(_core.LLVMPointerType(pointee_ty.ptr, 
                    addr_space), TYPE_POINTER)

    @staticmethod
    def vector(element_ty, count):
        """Create a vector type.

        Creates a type for a vector of elements of type `element_ty',
        having `count' elements."""
        check_is_type(element_ty)
        count = int(count) # must be an int
        return _make_type(_core.LLVMVectorType(element_ty.ptr, count), 
                    TYPE_VECTOR)

    @staticmethod
    def void():
        """Create a void type.

        Represents the `void' type."""
        return _make_type(_core.LLVMVoidType(), TYPE_VOID)

    @staticmethod
    def label():
        """Create a label type."""
        return _make_type(_core.LLVMLabelType(), TYPE_LABEL)

    @staticmethod
    def opaque():
        """Create an opaque type.

        Opaque types are used to create self-referencing types."""
        return _make_type(_core.LLVMOpaqueType(), TYPE_OPAQUE)

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods instead."""
        self.ptr = ptr
        self.kind = kind
        """An enum (int) value denoting which type this is.

        Use the symbolic constants TYPE_* defined in llvm.core
        module."""

    def __str__(self):
        """Text representation of a type.

        Returns the textual representation (`llvm assembly') of the type."""
        return _core.LLVMDumpTypeToString(self.ptr)

    def __eq__(self, rhs):
        if isinstance(rhs, Type):
            return str(self) == str(rhs)
        else:
            return False

    def refine(self, dest):
        """Refine the abstract type represented by self into a concrete class.

        This object is no longer valid after refining, so do not hold references
        to it after calling. See the user guide for examples on how to use this."""

        check_is_type(dest)
        _core.LLVMRefineType(self.ptr, dest.ptr)
        self.ptr = None


class IntegerType(Type):
    """Represents an integer type."""

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def width(self):
        """The width of the integer type, in bits."""
        return _core.LLVMGetIntTypeWidth(self.ptr)


class FunctionType(Type):
    """Represents a function type."""

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def return_type(self):
        """The type of the value returned by this function."""
        ptr  = _core.LLVMGetReturnType(self.ptr)
        kind = _core.LLVMGetTypeKind(ptr)
        return _make_type(ptr, kind)

    @property
    def vararg(self):
        """True if this function is variadic."""
        return _core.LLVMIsFunctionVarArg(self.ptr) != 0

    @property
    def args(self):
        """An iterable that yields Type objects, representing the types of the
        arguments accepted by this function, in order."""
        pp = _core.LLVMGetFunctionTypeParams(self.ptr)
        return [ _make_type(p, _core.LLVMGetTypeKind(p)) for p in pp ]

    @property
    def arg_count(self):
        """Number of arguments accepted by this function.

        Same as len(obj.args), but faster."""
        return _core.LLVMCountParamTypes(self.ptr)


class StructType(Type):

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def element_count(self):
        return _core.LLVMCountStructElementTypes(self.ptr)

    @property
    def elements(self):
        pp = _core.LLVMGetStructElementTypes(self.ptr)
        return [ _make_type(p, _core.LLVMGetTypeKind(p)) for p in pp ]

    @property
    def packed(self):
        return _core.LLVMIsPackedStruct(self.ptr) != 0


class ArrayType(Type):

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def element(self):
        ptr  = _core.LLVMGetElementType(self.ptr)
        kind = _core.LLVMGetTypeKind(ptr)
        return _make_type(ptr, kind)

    @property
    def count(self):
        return _core.LLVMGetArrayLength(self.ptr)


class PointerType(Type):

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def address_space(self):
        return _core.LLVMGetPointerAddressSpace(self.ptr)


class VectorType(Type):

    def __init__(self, ptr, kind):
        """DO NOT CALL DIRECTLY.

        Use one of the static methods of the *base* class (Type) instead."""
        Type.__init__(self, ptr, kind)

    @property
    def element(self):
        ptr  = _core.LLVMGetElementType(self.ptr)
        kind = _core.LLVMGetTypeKind(ptr)
        return _make_type(ptr, kind)

    @property
    def count(self):
        return _core.LLVMGetVectorSize(self.ptr)


def _make_type(ptr, kind):
    if kind == TYPE_INTEGER:
        return IntegerType(ptr, kind)
    elif kind == TYPE_FUNCTION:
        return FunctionType(ptr, kind)
    elif kind == TYPE_STRUCT:
        return StructType(ptr, kind)
    elif kind == TYPE_ARRAY:
        return ArrayType(ptr, kind)
    elif kind == TYPE_POINTER:
        return PointerType(ptr, kind)
    elif kind == TYPE_VECTOR:
        return VectorType(ptr, kind)
    else:
        return Type(ptr, kind)


#===----------------------------------------------------------------------===
# Type Handle
#===----------------------------------------------------------------------===


class TypeHandle(object):

    @staticmethod
    def new(abstract_ty):
        check_is_type(abstract_ty)
        return TypeHandle(_core.LLVMCreateTypeHandle(abstract_ty.ptr))

    def __init__(self, ptr):
        self.ptr = ptr

    def __del__(self):
        _core.LLVMDisposeTypeHandle(self.ptr)

    @property
    def type(self):
        ptr = _core.LLVMResolveTypeHandle(self.ptr)
        return _make_type(ptr, _core.LLVMGetTypeKind(ptr))


#===----------------------------------------------------------------------===
# Values
#===----------------------------------------------------------------------===


class Value(object):

    def __init__(self, ptr):
        self.ptr = ptr

    def __str__(self):
        return _core.LLVMDumpValueToString(self.ptr)

    def __eq__(self, rhs):
        if isinstance(rhs, Value):
            return str(self) == str(rhs)
        else:
            return False

    def get_name(self):
        return _core.LLVMGetValueName(self.ptr)

    def set_name(self, value):
        return _core.LLVMSetValueName(self.ptr, value)

    name = property(get_name, set_name)

    @property
    def type(self):
        ptr  = _core.LLVMTypeOf(self.ptr)
        kind = _core.LLVMGetTypeKind(ptr)
        return _make_type(ptr, kind)


class Constant(Value):

    @staticmethod
    def null(ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstNull(ty.ptr));
        
    @staticmethod
    def all_ones(ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstAllOnes(ty.ptr));
        
    @staticmethod
    def undef(ty):
        check_is_type(ty)
        return Constant(_core.LLVMGetUndef(ty.ptr));

    @staticmethod
    def int(ty, value):
        check_is_type(ty)
        return Constant(_core.LLVMConstInt(ty.ptr, value, 0))

    @staticmethod
    def int_signextend(ty, value):
        check_is_type(ty)
        return Constant(_core.LLVMConstInt(ty.ptr, value, 1))

    @staticmethod
    def real(ty, value):
        check_is_type(ty)
        if isinstance(value, str):
            return Constant(_core.LLVMConstRealOfString(ty.ptr, value))
        else:
            return Constant(_core.LLVMConstReal(ty.ptr, value))

    @staticmethod
    def string(strval): # dont_null_terminate=True
        return Constant(_core.LLVMConstString(strval, 1))
        
    @staticmethod
    def stringz(strval): # dont_null_terminate=False
        return Constant(_core.LLVMConstString(strval, 0))

    @staticmethod
    def array(ty, consts):
        check_is_type(ty)
        const_ptrs = unpack_constants(consts)
        return Constant(_core.LLVMConstArray(ty.ptr, const_ptrs))
        
    @staticmethod
    def struct(consts): # not packed
        const_ptrs = unpack_constants(consts)
        return Constant(_core.LLVMConstStruct(const_ptrs, 0))
    
    @staticmethod
    def packed_struct(consts):
        const_ptrs = unpack_constants(consts)
        return Constant(_core.LLVMConstStruct(const_ptrs, 1))
    
    @staticmethod
    def vector(consts):
        const_ptrs = unpack_constants(consts)
        return Constant(_core.LLVMConstVector(const_ptrs))

    @staticmethod
    def sizeof(ty):
        check_is_type(ty)
        return Constant(_core.LLVMSizeOf(ty.ptr))

    def __init__(self, ptr):
        Value.__init__(self, ptr)
        
    def neg(self):
        return Constant(_core.LLVMConstNeg(self.ptr))
        
    def not_(self):
        return Constant(_core.LLVMConstNot(self.ptr))
        
    def add(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstAdd(self.ptr, rhs.ptr))

    def sub(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstSub(self.ptr, rhs.ptr))

    def mul(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstMul(self.ptr, rhs.ptr))

    def udiv(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstUDiv(self.ptr, rhs.ptr))

    def sdiv(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstSDiv(self.ptr, rhs.ptr))

    def fdiv(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstFDiv(self.ptr, rhs.ptr))

    def urem(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstURem(self.ptr, rhs.ptr))

    def srem(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstSRem(self.ptr, rhs.ptr))

    def frem(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstFRem(self.ptr, rhs.ptr))

    def and_(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstAnd(self.ptr, rhs.ptr))

    def or_(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstOr(self.ptr, rhs.ptr))

    def xor(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstXor(self.ptr, rhs.ptr))

    def icmp(self, int_pred, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstICmp(int_pred, self.ptr, rhs.ptr))

    def fcmp(self, real_pred, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstFCmp(real_pred, self.ptr, rhs.ptr))

    def vicmp(self, int_pred, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstVICmp(int_pred, self.ptr, rhs.ptr))

    def vfcmp(self, real_pred, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstVFCmp(real_pred, self.ptr, rhs.ptr))

    def shl(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstShl(self.ptr, rhs.ptr))

    def lshr(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstLShr(self.ptr, rhs.ptr))

    def ashr(self, rhs):
        check_is_constant(rhs)
        return Constant(_core.LLVMConstAShr(self.ptr, rhs.ptr))

    def gep(self, indices):
        index_ptrs = unpack_constants(indices)
        return Constant(_core.LLVMConstGEP(self.ptr, index_ptrs))
    
    def trunc(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstTrunc(self.ptr, ty.ptr))

    def sext(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstSExt(self.ptr, ty.ptr))

    def zext(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstZExt(self.ptr, ty.ptr))

    def fptrunc(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstFPTrunc(self.ptr, ty.ptr))

    def fpext(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstFPExt(self.ptr, ty.ptr))

    def uitofp(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstUIToFP(self.ptr, ty.ptr))

    def sitofp(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstSIToFP(self.ptr, ty.ptr))

    def fptoui(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstFPToUI(self.ptr, ty.ptr))

    def fptosi(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstFPToSI(self.ptr, ty.ptr))

    def ptrtoint(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstPtrToInt(self.ptr, ty.ptr))

    def inttoptr(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstIntToPtr(self.ptr, ty.ptr))

    def bitcast(self, ty):
        check_is_type(ty)
        return Constant(_core.LLVMConstBitCast(self.ptr, ty.ptr))

    def select(self, true_const, false_const):
        check_is_constant(true_const)
        check_is_constant(false_const)
        return Constant(_core.LLVMConstSelect(self.ptr, true_const.ptr, false_const.ptr))

    def extract_element(self, index): # note: self must be a _vector_ constant
        check_is_constant(index)
        return Constant(_core.LLVMConstExtractElement(self.ptr, index.ptr))

    def insert_element(self, value, index): # note: self must be a _vector_ constant
        check_is_constant(value)
        check_is_constant(index)
        return Constant(_core.LLVMConstInsertElement(self.ptr, value.ptr, index.ptr))

    def shuffle_vector(self, vector_b, mask): # note: self must be a _vector_ constant
        check_is_constant(vector_b)   # note: vector_b must be a _vector_ constant
        check_is_constant(mask)
        return Constant(_core.LLVMConstShuffleVector(self.ptr, vector_b.ptr, mask.ptr))


class GlobalValue(Constant):

    def __init__(self, ptr, module):
        Constant.__init__(self, ptr)
        self._module = module # hang on to the module

    def _delete(self):
        self._module = None # set it free
        self.ptr = None

    def get_linkage(self): return _core.LLVMGetLinkage(self.ptr)
    def set_linkage(self, value): _core.LLVMSetLinkage(self.ptr, value)
    linkage = property(get_linkage, set_linkage)

    def get_section(self): return _core.LLVMGetSection(self.ptr)
    def set_section(self, value): return _core.LLVMSetSection(self.ptr, value)
    section = property(get_section, set_section)
    
    def get_visibility(self): return _core.LLVMGetVisibility(self.ptr)
    def set_visibility(self, value): return _core.LLVMSetVisibility(self.ptr, value)
    visibility = property(get_visibility, set_visibility)

    def get_alignment(self): return _core.LLVMGetAlignment(self.ptr)
    def set_alignment(self, value): return _core.LLVMSetAlignment(self.ptr, value)
    alignment = property(get_alignment, set_alignment)

    @property
    def is_declaration(self):
        return _core.LLVMIsDeclaration(self.ptr) != 0

    @property
    def module(self):
        return self._module


class GlobalVariable(GlobalValue):

    @staticmethod
    def new(module, ty, name):
        check_is_type(ty)
        return GlobalVariable(_core.LLVMAddGlobal(module.ptr, ty.ptr, name), module)

    @staticmethod
    def get(module, name):
        return GlobalVariable(_core.LLVMGetNamedGlobal(module.ptr, name), module)

    def __init__(self, ptr, module):
        GlobalValue.__init__(self, ptr, module)

    def delete(self):
        _core.LLVMDeleteGlobal(self.ptr)
        self._delete()

    def get_initializer(self):
        if _core.LLVMHasInitializer(self.ptr):
            return Constant(_core.LLVMGetInitializer(self.ptr))
        else:
            return None

    def set_initializer(self, const):
        check_is_constant(const)
        _core.LLVMSetInitializer(self.ptr, const.ptr)

    initializer = property(get_initializer, set_initializer)

    def get_is_global_constant(self):
        return _core.LLVMIsGlobalConstant(self.ptr)

    def set_is_global_constant(self, value):
        value = 1 if value else 0
        _core.LLVMSetGlobalConstant(self.ptr, value)

    global_constant = property(get_is_global_constant, set_is_global_constant)


class Argument(Value):

    def __init__(self, ptr):
        Value.__init__(self, ptr)

    def add_attribute(self, attr):
        _core.LLVMAddParamAttr(self.ptr, attr)

    def remove_attribute(self, attr):
        _core.LLVMRemoveParamAttr(self.ptr, attr)

    def set_alignment(self, align):
        _core.LLVMSetParamAlignment(self.ptr, align)


class Function(GlobalValue):

    @staticmethod
    def new(module, func_ty, name):
        return Function(_core.LLVMAddFunction(module.ptr, name, func_ty.ptr), module)

    @staticmethod
    def get(module, name):
        return Function(_core.LLVMGetNamedFunction(module.ptr, name), module)
    
    def __init__(self, ptr, module):
        GlobalValue.__init__(self, ptr, module)

    def delete(self):
        _core.LLVMDeleteFunction(self.ptr)
        self._delete()

    @property
    def intrinsic_id(self):
        return _core.LLVMGetIntrinsicID(self.ptr)

    def get_calling_convention(self): return _core.LLVMGetFunctionCallConv(self.ptr)
    def set_calling_convention(self, value): _core.LLVMSetFunctionCallConv(self.ptr, value)
    calling_convention = property(get_calling_convention, set_calling_convention)

    def get_collector(self): return _core.LLVMGetCollector(self.ptr)
    def set_collector(self, value): _core.LLVMSetCollector(self.ptr, value)
    collector = property(get_collector, set_collector)

    @property
    def args(self):
        return wrapiter(_core.LLVMGetFirstParam, _core.LLVMGetNextParam,
            self.ptr, Argument)

    @property
    def basic_block_count(self):
        return _core.LLVMCountBasicBlocks(self.ptr)

    def get_entry_basic_block(self):
        if self.basic_block_count == 0:
            return None
        return BasicBlock(_core.LLVMGetEntryBasicBlock(self.ptr))

    def append_basic_block(self, name):
        return BasicBlock(_core.LLVMAppendBasicBlock(self.ptr, name))

    @property
    def basic_blocks(self):
        return wrapiter(_core.LLVMGetFirstBasicBlock, 
            _core.LLVMGetNextBasicBlock, self.ptr, BasicBlock)

    def verify(self):
        # Although we're just asking LLVM to return the success or
        # failure, it appears to print result to stderr and abort.
        return _core.LLVMVerifyFunction(self.ptr) != 0


#===----------------------------------------------------------------------===
# Instruction
#===----------------------------------------------------------------------===

class Instruction(Value):

    def __init__(self, ptr):
        Value.__init__(self, ptr)

    @property
    def basic_block(self):
        return BasicBlock(_core.LLVMGetInstructionParent(self.ptr))


class CallOrInvokeInstruction(Instruction):

    def __init__(self, ptr):
        Instruction.__init__(self, ptr)

    def get_calling_convention(self): return _core.LLVMGetInstructionCallConv(self.ptr)
    def set_calling_convention(self, value): _core.LLVMSetInstructionCallConv(self.ptr, value)
    calling_convention = property(get_calling_convention, set_calling_convention)

    def add_parameter_attribute(self, idx, attr):
        _core.LLVMAddInstrParamAttr(self.ptr, idx, attr)

    def remove_parameter_attribute(self, idx, attr):
        _core.LLVMRemoveInstrParamAttr(self.ptr, idx, attr)

    def set_parameter_alignment(self, idx, align):
        _core.LLVMSetInstrParamAlignment(self.ptr, idx, align)


class PHINode(Instruction):

    def __init__(self, ptr):
        Instruction.__init__(self, ptr)

    @property
    def incoming_count(self):
        return _core.LLVMCountIncoming(self.ptr)

    def add_incoming(self, value, block):
        check_is_value(value)
        check_is_basic_block(block)
        _core.LLVMAddIncoming1(self.ptr, value.ptr, block.ptr)

    def get_incoming_value(self, idx):
        return Value(_core.LLVMGetIncomingValue(self.ptr, idx))

    def get_incoming_block(self, idx):
        return BasicBlock(_core.LLVMGetIncomingBlock(self.ptr, idx))


class SwitchInstruction(Instruction):

    def __init__(self, ptr):
        Instruction.__init__(self, ptr)

    def add_case(self, const, bblk):
        check_is_constant(const) # and has to be an int too
        check_is_basic_block(bblk)
        _core.LLVMAddCase(self.ptr, const.ptr, bblk.ptr)


#===----------------------------------------------------------------------===
# Basic block
#===----------------------------------------------------------------------===


class BasicBlock(Value):

    def __init__(self, ptr):
        self.ptr = ptr

    def insert_before(self, name):
        return BasicBlock(_core.LLVMInsertBasicBlock(self.ptr, name))

    def delete(self):
        _core.LLVMDeleteBasicBlock(self.ptr)
        self.ptr = None

    @property
    def function(self):
        return Function(_core.LLVMGetBasicBlockParent(self.ptr))

    @property
    def instructions(self):
        return wrapiter(_core.LLVMGetFirstInstruction,
            _core.LLVMGetNextInstruction, self.ptr, Instruction)


#===----------------------------------------------------------------------===
# Builder
#===----------------------------------------------------------------------===

        
class Builder(object):

    @staticmethod
    def new(basic_block):
        check_is_basic_block(basic_block)
        b = Builder(_core.LLVMCreateBuilder())
        b.position_at_end(basic_block)
        return b
        
    def __init__(self, ptr):
        self.ptr = ptr

    def __del__(self):
        _core.LLVMDisposeBuilder(self.ptr)

    def position_at_beginning(self, bblk):
        """Position the builder at the beginning of the given block.
        
        Next instruction inserted will be first one in the block."""
        # Avoids using "blk.instructions", which will fetch all the
        # instructions into a list. Don't try this at home, though.
        first_inst = Instruction(_core.LLVMGetFirstInstruction(self.block.ptr))
        self.position_before(first_inst)

    def position_at_end(self, bblk):
        """Position the builder at the end of the given block.

        Next instruction inserted will be last one in the block."""
        _core.LLVMPositionBuilderAtEnd(self.ptr, bblk.ptr)

    def position_before(self, instr):
        """Position the builder before the given instruction.

        The instruction can belong to a basic block other than the
        current one."""
        _core.LLVMPositionBuilderBefore(self.ptr, instr.ptr)

    @property
    def block(self):
        return BasicBlock(_core.LLVMGetInsertBlock(self.ptr))

    # terminator instructions

    def ret_void(self):
        return Instruction(_core.LLVMBuildRetVoid(self.ptr))

    def ret(self, value):
        check_is_value(value)
        return Instruction(_core.LLVMBuildRet(self.ptr, value.ptr))

    def ret_many(self, values):
        vs = unpack_values(values)
        return Instruction(_core.LLVMBuildRetMultiple(self.ptr, vs))

    def branch(self, bblk):
        check_is_basic_block(bblk)
        return Instruction(_core.LLVMBuildBr(self.ptr, bblk.ptr))
        
    def cbranch(self, if_value, then_blk, else_blk):
        check_is_value(if_value)
        check_is_basic_block(then_blk)
        check_is_basic_block(else_blk)
        return Instruction(_core.LLVMBuildCondBr(self.ptr, if_value.ptr, then_blk.ptr, else_blk.ptr))
        
    def switch(self, value, else_blk, n=10):
        check_is_value(value)  # value has to be of any 'int' type
        check_is_basic_block(else_blk)
        return SwitchInstruction(_core.LLVMBuildSwitch(self.ptr, value.ptr, else_blk.ptr, n))
        
    def invoke(self, func, args, then_blk, catch_blk, name=""):
        check_is_function(func)
        check_is_basic_block(then_blk)
        check_is_basic_block(catch_blk)
        args2 = unpack_values(args)
        return CallOrInvokeInstruction(_core.LLVMBuildInvoke(self.ptr, func.ptr, args2, then_blk.ptr, catch_blk.ptr, name))

    def unwind(self):
        return Instruction(_core.LLVMBuildUnwind(self.ptr))
        
    def unreachable(self):
        return Instruction(_core.LLVMBuildUnreachable(self.ptr))

    # arithmethic, bitwise and logical
    
    def add(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildAdd(self.ptr, lhs.ptr, rhs.ptr, name))
        
    def sub(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildSub(self.ptr, lhs.ptr, rhs.ptr, name))

    def mul(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildMul(self.ptr, lhs.ptr, rhs.ptr, name))

    def udiv(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildUDiv(self.ptr, lhs.ptr, rhs.ptr, name))

    def sdiv(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildSDiv(self.ptr, lhs.ptr, rhs.ptr, name))

    def fdiv(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildFDiv(self.ptr, lhs.ptr, rhs.ptr, name))

    def urem(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildURem(self.ptr, lhs.ptr, rhs.ptr, name))

    def srem(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildSRem(self.ptr, lhs.ptr, rhs.ptr, name))

    def frem(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildFRem(self.ptr, lhs.ptr, rhs.ptr, name))

    def shl(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildShl(self.ptr, lhs.ptr, rhs.ptr, name))

    def lshr(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildLShr(self.ptr, lhs.ptr, rhs.ptr, name))

    def ashr(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildAShr(self.ptr, lhs.ptr, rhs.ptr, name))

    def and_(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildAnd(self.ptr, lhs.ptr, rhs.ptr, name))

    def or_(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildOr(self.ptr, lhs.ptr, rhs.ptr, name))

    def xor(self, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildXor(self.ptr, lhs.ptr, rhs.ptr, name))

    def neg(self, val, name=""):
        check_is_value(val)
        return Instruction(_core.LLVMBuildNeg(self.ptr, val.ptr, name))

    def not_(self, val, name=""):
        check_is_value(val)
        return Instruction(_core.LLVMBuildNot(self.ptr, val.ptr, name))

    # memory

    def malloc(self, ty, name=""):
        check_is_type(ty)
        return Instruction(_core.LLVMBuildMalloc(self.ptr, ty.ptr, name))

    def malloc_array(self, ty, size, name=""):
        check_is_type(ty)
        check_is_value(size)
        return Instruction(_core.LLVMBuildArrayMalloc(self.ptr, ty.ptr, size.ptr, name))

    def alloca(self, ty, name=""):
        check_is_type(ty)
        return Instruction(_core.LLVMBuildAlloca(self.ptr, ty.ptr, name))

    def alloca_array(self, ty, size, name=""):
        check_is_type(ty)
        check_is_value(size)
        return Instruction(_core.LLVMBuildArrayAlloca(self.ptr, ty.ptr, size.ptr, name))

    def free(self, ptr):
        check_is_value(ptr)
        return Instruction(_core.LLVMBuildFree(self.ptr, ptr.ptr))

    def load(self, ptr, name=""):
        check_is_value(ptr)
        return Instruction(_core.LLVMBuildLoad(self.ptr, ptr.ptr, name))

    def store(self, value, ptr):
        check_is_value(value)
        check_is_value(ptr)
        return Instruction(_core.LLVMBuildStore(self.ptr, value.ptr, ptr.ptr))

    def gep(self, ptr, indices, name=""):
        check_is_value(ptr)
        index_ptrs = unpack_values(indices)
        return Value(_core.LLVMBuildGEP(self.ptr, ptr.ptr, index_ptrs, name))

    # casts and extensions

    def trunc(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildTrunc(self.ptr, value.ptr, dest_ty.ptr, name))

    def zext(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildZExt(self.ptr, value.ptr, dest_ty.ptr, name))

    def sext(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildSExt(self.ptr, value.ptr, dest_ty.ptr, name))

    def fptoui(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildFPToUI(self.ptr, value.ptr, dest_ty.ptr, name))

    def fptosi(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildFPToSI(self.ptr, value.ptr, dest_ty.ptr, name))

    def uitofp(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildUIToFP(self.ptr, value.ptr, dest_ty.ptr, name))

    def sitofp(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildSIToFP(self.ptr, value.ptr, dest_ty.ptr, name))

    def fptrunc(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildFPTrunc(self.ptr, value.ptr, dest_ty.ptr, name))

    def fpext(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildFPExt(self.ptr, value.ptr, dest_ty.ptr, name))

    def ptrtoint(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildPtrToInt(self.ptr, value.ptr, dest_ty.ptr, name))

    def inttoptr(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildIntToPtr(self.ptr, value.ptr, dest_ty.ptr, name))

    def bitcast(self, value, dest_ty, name=""):
        check_is_value(value)
        check_is_type(dest_ty)
        return Value(_core.LLVMBuildBitCast(self.ptr, value.ptr, dest_ty.ptr, name))

    # comparisons

    def icmp(self, ipred, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildICmp(self.ptr, ipred, lhs.ptr, rhs.ptr, name))
        
    def fcmp(self, rpred, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildFCmp(self.ptr, rpred, lhs.ptr, rhs.ptr, name))

    def vicmp(self, ipred, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildVICmp(self.ptr, ipred, lhs.ptr, rhs.ptr, name))
        
    def vfcmp(self, rpred, lhs, rhs, name=""):
        check_is_value(lhs)
        check_is_value(rhs)
        return Value(_core.LLVMBuildVFCmp(self.ptr, rpred, lhs.ptr, rhs.ptr, name))

    # misc

    def getresult(self, retval, idx, name=""):
        check_is_value(retval)
        return PHINode(_core.LLVMBuildGetResult(self.ptr, retval.ptr, idx, name))

    def phi(self, ty, name=""):
        check_is_type(ty)
        return PHINode(_core.LLVMBuildPhi(self.ptr, ty.ptr, name))
        
    def call(self, fn, args, name=""):
        check_is_function(fn)
        arg_ptrs = unpack_values(args)
        return CallOrInvokeInstruction(_core.LLVMBuildCall(self.ptr, fn.ptr, arg_ptrs, name))
        
    def select(self, cond, then_blk, else_blk, name=""):
        check_is_value(cond)
        check_is_basic_block(then_blk)
        check_is_basic_block(else_blk)
        return Value(_core.LLVMBuildSelect(self.ptr, cond.ptr, then_blk.ptr, else_blk.ptr, name))
    
    def vaarg(self, list_val, ty, name=""):
        check_is_value(list_val)
        check_is_type(ty)
        return Instruction(_core.LLVMBuildVAArg(self.ptr, list_val.ptr, ty.ptr, name))
    
    def extract_element(self, vec_val, idx_val, name=""):
        check_is_value(vec_val)
        check_is_value(idx_val)
        return Value(_core.LLVMBuildExtractElement(self.ptr, vec_val.ptr, idx_val.ptr, name))
    
    def insert_element(self, vec_val, elt_val, idx_val, name=""):
        check_is_value(vec_val)
        check_is_value(elt_val)
        check_is_value(idx_val)
        return Value(_core.LLVMBuildInsertElement(self.ptr, vec_val.ptr, elt_val.ptr, idx_val.ptr, name))

    def shuffle_vector(self, vecA, vecB, mask, name=""):
        check_is_value(vecA)
        check_is_value(vecB)
        check_is_value(mask)
        return Value(_core.LLVMBuildShuffleVector(self.ptr, vecA.ptr, vecB.ptr, mask.ptr, name))


#===----------------------------------------------------------------------===
# Module provider
#===----------------------------------------------------------------------===


class ModuleProvider(llvm.Ownable):

    @staticmethod
    def new(module):
        check_is_module(module)
        check_is_unowned(module)
        return ModuleProvider(
            _core.LLVMCreateModuleProviderForExistingModule(module.ptr),
            module)

    def __init__(self, ptr, module):
        llvm.Ownable.__init__(self, ptr, _core.LLVMDisposeModuleProvider)
        module._own(self)
        # a module provider is both a owner (of modules) and an ownable
        # (can be owned by execution engines)


#===----------------------------------------------------------------------===
# Memory buffer
#===----------------------------------------------------------------------===


class MemoryBuffer(object):

    @staticmethod
    def from_file(self, fname):
        ret = _core.LLVMCreateMemoryBufferWithContentsOfFile(fname)
        if isinstance(ret, str):
            return (None, ret)
        else:
            obj = MemoryBuffer(ret)
            return (obj, "")

    @staticmethod
    def from_stdin(self):
        ret = _core.LLVMCreateMemoryBufferWithSTDIN()
        if isinstance(ret, str):
            return (None, ret)
        else:
            obj = MemoryBuffer(ret)
            return (obj, "")

    def __init__(self, ptr):
        self.ptr = ptr

    def __del__(self):
        _core.LLVMDisposeMemoryBuffer(self.ptr)

